/*
 *  Copyright (C) 2012 Компания ВИТ. web: http://diasled.ru Е-mail:  vit@diasled.ru
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ru.apertum.diasled.forms;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.jdesktop.application.Action;
import ru.apertum.diasled.common.Player;
import ru.apertum.diasled.common.Uses;
import ru.apertum.diasled.common.Uses.ClientException;
import ru.apertum.diasled.model.Examination;
import ru.apertum.diasled.model.Visit;
import ru.apertum.diasled.model.exam.InsideElements;
import ru.apertum.diasled.model.exam.Shoes;
import ru.apertum.diasled.model.exam.TypeExam;
import ru.apertum.diasled.model.load.Loader;
import ru.apertum.diasled.cfg.Configuration;
import ru.apertum.diasled.rxtx.PortUtil;

/**
 * Мастер проведения обследования. Ведет врача по шагам от старта до финиша.
 * @author Evgeniy Egorov, Aperum Projects
 */
public class FExamMaster extends javax.swing.JDialog {

    final private Visit visit;
    final private FExamMaster examMaster = this;
    public static JLabel hackState;

    /** Creates new form FExamMaster */
    public FExamMaster(java.awt.Frame parent, boolean modal, Visit visit) {
        super(parent, modal);
        initComponents();
        this.visit = visit;
        hackState = labelState;
        try {
            setIconImage(ImageIO.read(DiasledView.class.getResource("/ru/apertum/diasled/forms/resources/favicon.png")));
        } catch (IOException ex) {
            System.err.println(ex);
        }
        // свернем по esc
        getRootPane().registerKeyboardAction(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (timer != null) {
                    timer.stop();
                }
                setVisible(false);
            }
        },
                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);

        labelProgress.setText("");
        labelState.setText("");
        comboBoxShoesKind.setModel(Shoes.getInstance());
        comboBoxInsideKind.setModel(InsideElements.getInstance());
        comboBoxExamKind.setModel(TypeExam.getInstance());

        sliderMetronome.getModel().addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                labelMetronome.setText(sliderMetronome.getModel().getValue() + " шагов в минуту    " + new BigDecimal(sliderMetronome.getModel().getValue() / 60.000001).setScale(1, RoundingMode.UP).doubleValue() + " шага в секунду");
            }
        });
        sliderMetronome.getModel().setValue(80);
        labelMetronome.setText(sliderMetronome.getModel().getValue() + " шагов в минуту    " + new BigDecimal(sliderMetronome.getModel().getValue() / 60.000001).setScale(1, RoundingMode.UP).doubleValue() + " шага в секунду");

        comboBoxMetronome.getModel().addListDataListener(new ListDataListener() {

            @Override
            public void intervalAdded(ListDataEvent e) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public void intervalRemoved(ListDataEvent e) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public void contentsChanged(ListDataEvent e) {
                sliderMetronome.getModel().setValue(comboBoxMetronome.getSelectedIndex() * 20 + 30);
            }
        });


        sliderTime.getModel().addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                labelTime.setText("Сигнал к началу. " + (comboBoxMinutes.getSelectedIndex() == 0 ? "" : comboBoxMinutes.getModel().getSelectedItem() + " ") + sliderTime.getModel().getValue() + " секунд на разгон и ходьбу. 5 секунд на остановку и стабилизацию статики");
            }
        });
        sliderTime.getModel().setValue(10);
        labelTime.setText("Сигнал к началу. " + (comboBoxMinutes.getSelectedIndex() == 0 ? "" : comboBoxMinutes.getModel().getSelectedItem() + " ") + sliderTime.getModel().getValue() + " секунд на разгон и ходьбу. 5 секунд на остановку и стабилизацию статики");
        comboBoxTime.getModel().addListDataListener(new ListDataListener() {

            @Override
            public void intervalAdded(ListDataEvent e) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public void intervalRemoved(ListDataEvent e) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public void contentsChanged(ListDataEvent e) {
                sliderTime.getModel().setValue(comboBoxTime.getSelectedIndex() == 0 ? 3 : (comboBoxTime.getSelectedIndex() * 10));
            }
        });
        buttonBreakExam.setVisible(false);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        panelBattons = new javax.swing.JPanel();
        buttonStart = new javax.swing.JButton();
        buttonCancel = new javax.swing.JButton();
        labelProgress = new javax.swing.JLabel();
        labelState = new javax.swing.JLabel();
        buttonBreakExam = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        comboBoxShoesKind = new javax.swing.JComboBox();
        comboBoxInsideKind = new javax.swing.JComboBox();
        jLabel2 = new javax.swing.JLabel();
        comboBoxExamKind = new javax.swing.JComboBox();
        jLabel3 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        textAreaExamDescription = new javax.swing.JTextArea();
        jPanel5 = new javax.swing.JPanel();
        comboBoxMinutes = new javax.swing.JComboBox();
        jLabel6 = new javax.swing.JLabel();
        comboBoxTime = new javax.swing.JComboBox();
        jLabel9 = new javax.swing.JLabel();
        sliderTime = new javax.swing.JSlider();
        labelTime = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        checkBoxMetronome = new javax.swing.JCheckBox();
        comboBoxMetroKind = new javax.swing.JComboBox();
        jLabel7 = new javax.swing.JLabel();
        comboBoxMetronome = new javax.swing.JComboBox();
        jLabel8 = new javax.swing.JLabel();
        sliderMetronome = new javax.swing.JSlider();
        toggleButtonTestMetronome = new javax.swing.JToggleButton();
        labelMetronome = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(ru.apertum.diasled.Diasled.class).getContext().getResourceMap(FExamMaster.class);
        setTitle(resourceMap.getString("Form.title")); // NOI18N
        setAlwaysOnTop(true);
        setModal(true);
        setName("Form"); // NOI18N
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowDeactivated(java.awt.event.WindowEvent evt) {
                formWindowDeactivated(evt);
            }
        });

        panelBattons.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
        panelBattons.setName("panelBattons"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(ru.apertum.diasled.Diasled.class).getContext().getActionMap(FExamMaster.class, this);
        buttonStart.setAction(actionMap.get("startExam")); // NOI18N
        buttonStart.setBackground(resourceMap.getColor("buttonStart.background")); // NOI18N
        buttonStart.setName("buttonStart"); // NOI18N

        buttonCancel.setAction(actionMap.get("cancelExam")); // NOI18N
        buttonCancel.setBackground(resourceMap.getColor("buttonCancel.background")); // NOI18N
        buttonCancel.setName("buttonCancel"); // NOI18N

        labelProgress.setFont(resourceMap.getFont("labelProgress.font")); // NOI18N
        labelProgress.setForeground(resourceMap.getColor("labelProgress.foreground")); // NOI18N
        labelProgress.setText(resourceMap.getString("labelProgress.text")); // NOI18N
        labelProgress.setName("labelProgress"); // NOI18N

        labelState.setFont(resourceMap.getFont("labelState.font")); // NOI18N
        labelState.setText(resourceMap.getString("labelState.text")); // NOI18N
        labelState.setName("labelState"); // NOI18N

        buttonBreakExam.setAction(actionMap.get("breakExam")); // NOI18N
        buttonBreakExam.setBackground(resourceMap.getColor("buttonBreakExam.background")); // NOI18N
        buttonBreakExam.setName("buttonBreakExam"); // NOI18N

        javax.swing.GroupLayout panelBattonsLayout = new javax.swing.GroupLayout(panelBattons);
        panelBattons.setLayout(panelBattonsLayout);
        panelBattonsLayout.setHorizontalGroup(
            panelBattonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelBattonsLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(labelProgress)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 117, Short.MAX_VALUE)
                .addComponent(labelState, javax.swing.GroupLayout.PREFERRED_SIZE, 173, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(buttonBreakExam)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(buttonCancel)
                .addGap(18, 18, 18)
                .addComponent(buttonStart)
                .addContainerGap())
        );
        panelBattonsLayout.setVerticalGroup(
            panelBattonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelBattonsLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panelBattonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(buttonStart)
                    .addComponent(labelProgress)
                    .addComponent(buttonCancel)
                    .addComponent(buttonBreakExam))
                .addContainerGap())
            .addComponent(labelState, javax.swing.GroupLayout.DEFAULT_SIZE, 47, Short.MAX_VALUE)
        );

        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("jPanel2.border.title"))); // NOI18N
        jPanel2.setName("jPanel2"); // NOI18N

        jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
        jLabel1.setName("jLabel1"); // NOI18N

        comboBoxShoesKind.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "В носках", "В стандартной", "В обуви пациента", "В ортопедической обуви №1", "В ортопедической обуви №2", "Нестандартные условия" }));
        comboBoxShoesKind.setName("comboBoxShoesKind"); // NOI18N

        comboBoxInsideKind.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Без вкладных элементов", "Вкладные элементы №1", "Вкладные элементы №2", "Вкладные элементы №3", "Нестандартные условия", " " }));
        comboBoxInsideKind.setName("comboBoxInsideKind"); // NOI18N

        jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
        jLabel2.setName("jLabel2"); // NOI18N

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addGap(18, 18, 18)
                        .addComponent(comboBoxShoesKind, 0, 589, Short.MAX_VALUE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addGap(18, 18, 18)
                        .addComponent(comboBoxInsideKind, 0, 512, Short.MAX_VALUE)))
                .addContainerGap())
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(comboBoxShoesKind, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(comboBoxInsideKind, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        );

        comboBoxExamKind.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Тестовое", "Стоя", "Ходьба медленная", "Ходьба средняя", "Ходьба быстрая", "Бег или другое" }));
        comboBoxExamKind.setName("comboBoxExamKind"); // NOI18N

        jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
        jLabel3.setName("jLabel3"); // NOI18N

        jScrollPane1.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("jScrollPane1.border.title"))); // NOI18N
        jScrollPane1.setName("jScrollPane1"); // NOI18N

        textAreaExamDescription.setColumns(20);
        textAreaExamDescription.setFont(resourceMap.getFont("textAreaExamDescription.font")); // NOI18N
        textAreaExamDescription.setRows(1);
        textAreaExamDescription.setText(resourceMap.getString("textAreaExamDescription.text")); // NOI18N
        textAreaExamDescription.setWrapStyleWord(true);
        textAreaExamDescription.setName("textAreaExamDescription"); // NOI18N
        jScrollPane1.setViewportView(textAreaExamDescription);

        jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("jPanel5.border.title"))); // NOI18N
        jPanel5.setName("jPanel5"); // NOI18N

        comboBoxMinutes.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "0 минут", "1 минута", "2 минуты", "3 минуты", "4 минуты", "6 минут", "7 минут", "8 минут", "9 минут", "10 минут", "11 минут", "12 минут", "13 минут", "14 минут", "15 минут", "16 минут", "17 минут", "18 минут", "19 минут", "20 минут" }));
        comboBoxMinutes.setName("comboBoxMinutes"); // NOI18N
        comboBoxMinutes.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxMinutesActionPerformed(evt);
            }
        });

        jLabel6.setText(resourceMap.getString("jLabel6.text")); // NOI18N
        jLabel6.setName("jLabel6"); // NOI18N

        comboBoxTime.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "3 секунды", "10 секунд", "20 секунд", "30 секунд", "40 секунд", "50 секунд", "60 секунд" }));
        comboBoxTime.setSelectedIndex(1);
        comboBoxTime.setName("comboBoxTime"); // NOI18N

        jLabel9.setText(resourceMap.getString("jLabel9.text")); // NOI18N
        jLabel9.setName("jLabel9"); // NOI18N

        sliderTime.setMaximum(60);
        sliderTime.setMinimum(3);
        sliderTime.setValue(10);
        sliderTime.setName("sliderTime"); // NOI18N

        labelTime.setText(resourceMap.getString("labelTime.text")); // NOI18N
        labelTime.setName("labelTime"); // NOI18N

        javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
        jPanel5.setLayout(jPanel5Layout);
        jPanel5Layout.setHorizontalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(labelTime)
                    .addGroup(jPanel5Layout.createSequentialGroup()
                        .addComponent(comboBoxMinutes, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(jLabel6)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(sliderTime, javax.swing.GroupLayout.DEFAULT_SIZE, 344, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel9)
                        .addGap(18, 18, 18)
                        .addComponent(comboBoxTime, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );
        jPanel5Layout.setVerticalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(comboBoxMinutes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel6)
                        .addComponent(comboBoxTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel9))
                    .addComponent(sliderTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(labelTime))
        );

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("jPanel1.border.title"))); // NOI18N
        jPanel1.setName("jPanel1"); // NOI18N

        checkBoxMetronome.setSelected(true);
        checkBoxMetronome.setText(resourceMap.getString("checkBoxMetronome.text")); // NOI18N
        checkBoxMetronome.setName("checkBoxMetronome"); // NOI18N

        comboBoxMetroKind.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "1", "2", "3" }));
        comboBoxMetroKind.setName("comboBoxMetroKind"); // NOI18N

        jLabel7.setText(resourceMap.getString("jLabel7.text")); // NOI18N
        jLabel7.setName("jLabel7"); // NOI18N

        comboBoxMetronome.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "30 шагов в минуту", "50 шагов в минуту", "70 шагов в минуту", "90 шагов в минуту", "110 шагов в минуту", "130 шагов в минуту", "150 шагов в минуту", "170 шагов в минуту" }));
        comboBoxMetronome.setName("comboBoxMetronome"); // NOI18N
        comboBoxMetronome.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxMetronomeActionPerformed(evt);
            }
        });

        jLabel8.setText(resourceMap.getString("jLabel8.text")); // NOI18N
        jLabel8.setName("jLabel8"); // NOI18N

        sliderMetronome.setMaximum(180);
        sliderMetronome.setMinimum(30);
        sliderMetronome.setSnapToTicks(true);
        sliderMetronome.setToolTipText(resourceMap.getString("sliderMetronome.toolTipText")); // NOI18N
        sliderMetronome.setBorder(new javax.swing.border.MatteBorder(null));
        sliderMetronome.setName("sliderMetronome"); // NOI18N
        sliderMetronome.setValueIsAdjusting(true);

        toggleButtonTestMetronome.setText(resourceMap.getString("toggleButtonTestMetronome.text")); // NOI18N
        toggleButtonTestMetronome.setBorderPainted(false);
        toggleButtonTestMetronome.setName("toggleButtonTestMetronome"); // NOI18N
        toggleButtonTestMetronome.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                toggleButtonTestMetronomeActionPerformed(evt);
            }
        });

        labelMetronome.setText(resourceMap.getString("labelMetronome.text")); // NOI18N
        labelMetronome.setName("labelMetronome"); // NOI18N

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(checkBoxMetronome)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(comboBoxMetroKind, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel7)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(sliderMetronome, javax.swing.GroupLayout.DEFAULT_SIZE, 478, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel8)
                        .addGap(18, 18, 18)
                        .addComponent(comboBoxMetronome, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                        .addComponent(labelMetronome)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 512, Short.MAX_VALUE)
                        .addComponent(toggleButtonTestMetronome)))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(checkBoxMetronome)
                    .addComponent(comboBoxMetroKind, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel7)
                        .addComponent(comboBoxMetronome, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel8))
                    .addComponent(sliderMetronome, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(toggleButtonTestMetronome)
                    .addComponent(labelMetronome)))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(10, 10, 10)
                .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGap(10, 10, 10))
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel3)
                .addGap(18, 18, 18)
                .addComponent(comboBoxExamKind, 0, 519, Short.MAX_VALUE)
                .addContainerGap())
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(panelBattons, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(comboBoxExamKind, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 72, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(panelBattons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void comboBoxMetronomeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxMetronomeActionPerformed
    }//GEN-LAST:event_comboBoxMetronomeActionPerformed
    private Timer timer;
    private void toggleButtonTestMetronomeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleButtonTestMetronomeActionPerformed
        if (!toggleButtonTestMetronome.isSelected()) {
            if (timer != null) {
                timer.stop();
                System.out.println("timer.stop();");
            }
            toggleButtonTestMetronome.setText("Тест метронома");
        } else {
            if (timer != null) {
                timer.stop();
            }
            toggleButtonTestMetronome.setText("Остановить");
            Player.playAudioResource("/metronome" + comboBoxMetroKind.getModel().getSelectedItem() + ".wav");
            timer = new Timer((int) Math.round(60.0000001 / sliderMetronome.getModel().getValue() * 1000.0000000001), new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    Player.playAudioResource("/metronome" + comboBoxMetroKind.getModel().getSelectedItem() + ".wav");
                }
            });
            timer.start();
        }
    }//GEN-LAST:event_toggleButtonTestMetronomeActionPerformed
    private boolean doExam = false;
    private void formWindowDeactivated(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowDeactivated
        if (doExam) {
            setVisible(true);
        } else {
            if (timer != null) {
                timer.stop();
            }
        }
    }//GEN-LAST:event_formWindowDeactivated

    private void comboBoxMinutesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxMinutesActionPerformed
        labelTime.setText("Сигнал к началу. " + (comboBoxMinutes.getSelectedIndex() == 0 ? "" : comboBoxMinutes.getModel().getSelectedItem() + " ") + sliderTime.getModel().getValue() + " секунд на разгон и ходьбу. 5 секунд на остановку и стабилизацию статики");
    }//GEN-LAST:event_comboBoxMinutesActionPerformed
    public Examination result = null;

    @Action
    public void startExam() {
        labelProgress.setText("Подготовка устройства...");
        final Configuration cfg = PortUtil.getConfiguration();
        if (cfg == null) {
            labelProgress.setText("Неудача");
            JOptionPane.showMessageDialog(this,
                    "Устройство не определено. Возможно оно не подключено. Подключите/переподключите устройство.",
                    "Определение устройства",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        final String port;
        if ((port = PortUtil.prepareDevice(cfg)) == null) {
            labelProgress.setText("Неудача");
            JOptionPane.showMessageDialog(this,
                    "Устройство определено, но не удалось его подготовить для обследования. Переподключите устройство.",
                    "Определение устройства",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        labelProgress.setText("Создание...");
        result = new Examination();
        result.setSysNum(cfg.getSysNum());
        result.setComments(textAreaExamDescription.getText());
        result.setShoesType(comboBoxShoesKind.getSelectedIndex());
        result.setInsideElement(comboBoxInsideKind.getSelectedIndex());
        result.setExamType(comboBoxExamKind.getSelectedIndex());
        result.setStepsРerMinute(sliderMetronome.getValue());
        result.setDuration(comboBoxMinutes.getSelectedIndex() * 60 + sliderTime.getValue());
        result.setVisit(visit);
        try {
            Examination.saveExam(result);
        } catch (ClientException ex) {
            labelProgress.setText("Неудача");
            JOptionPane.showMessageDialog(this,
                    "Ошибка сохранения обследования.",
                    "Сохранение обследования",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        labelProgress.setText("Старт...");
        buttonCancel.setVisible(false);
        buttonStart.setVisible(false);
        buttonBreakExam.setVisible(true);
        passExam(result, cfg, port);
    }

    @Action
    public void cancelExam() {
        if (timer != null) {
            timer.stop();
        }
        setVisible(false);
    }
    private Loader loader = null;
    private Timer forLab = null;
    private java.util.Timer steps = null;

    private void passExam(Examination exam, Configuration cfg, String portName) {
        doExam = true;
        Player.playAudioResource("/notify.wav");
        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {
            doExam = false;
            System.err.println(ex);
        }

        // старт метронома
        if (checkBoxMetronome.getModel().isSelected()) {
            timer = new Timer((int) Math.round(60.0000001 / sliderMetronome.getModel().getValue() * 1000.0000000001), new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    Player.playAudioResource("/metronome" + comboBoxMetroKind.getModel().getSelectedItem() + ".wav");
                }
            });
            timer.start();
        }

        // это просто отображение прогресса
        final int period = 300;
        forLab = new Timer(period, new ActionListener() {

            int i = 0;

            @Override
            public void actionPerformed(ActionEvent e) {
                labelProgress.setText("Идет обследование. Осталось секунд: " + ((comboBoxMinutes.getSelectedIndex() * 60 + sliderTime.getValue()) * 1000 - period * i++) / 1000 + "...");
            }
        });
        forLab.start();


        // загрузчик данных со сканера в БД
        loader = new Loader(exam, portName, (comboBoxMinutes.getSelectedIndex() * 60 + sliderTime.getValue()) * 100 + 5 * 100/*время остановки*/);
        loader.start();
        WAIT_FOR_FINISH.lock();


        // включим таймер, он сработает в конце обследования, а метод благополучно завершится.
        steps = new java.util.Timer(true);
        steps.schedule(new TimerTask() {

            @Override
            public void run() {
                tt.stop();
                // останавливаем метроном
                if (timer != null) {
                    timer.stop();
                }
                // останавливаем отображение прогресса
                forLab.stop();
                labelProgress.setText("Период стабилизации. Ждем стоя 5 секунд...");
                Player.playAudioResource("/stop.wav");
                // Определяем таймер общей остановки обследования
                steps.schedule(new TimerTask() {

                    @Override
                    public void run() {
                        labelProgress.setText("Ожидание завершения сохранения данных...");

                        Player.playAudioResource("/finished.wav");
                        // всех оповестили о завершении и ждем завершения процессов генерации и сохранения
                        // доступ к блокировке будет доступен после полного сохранения
                        WAIT_FOR_FINISH.lock();
                        WAIT_FOR_FINISH.unlock();
                        doExam = false;
                        if (loader.getResult()) {
                            labelProgress.setText("Обследование завершено успешно.");
                            // диолог о завершении и закрытие модальной формы проведения эксперименда
                            JOptionPane.showMessageDialog(examMaster,
                                    "Обследование завершено успешно.",
                                    "Завершение обследования",
                                    JOptionPane.INFORMATION_MESSAGE);
                        } else {
                            Uses.log.logger.error("Что-то пошло не так во время эксперимента.");
                            Examination.removeExam(result);
                            result = null;
                        }
                        setVisible(false);
                        // по закрытию модального диалога будет открыта форма отображения эксперимента
                    }
                }, 5000);
            }
        }, (comboBoxMinutes.getSelectedIndex() * 60 + sliderTime.getValue()) * 1000);

        // это таймер слежения за процессом, если упало, то он все закроет и форму закроет
        final Timer forErr = new Timer(200, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (loader.getRes() == 2) {
                    Uses.log.logger.error("Эксперимент упал. Сбой прекращает течение обследования.");
                    tt.stop();
                    doExam = false;
                    steps.cancel();
                    // останавливаем метроном
                    if (timer != null) {
                        timer.stop();
                    }
                    // останавливаем отображение прогресса
                    forLab.stop();
                    labelProgress.setText("Сбой обследования");
                    Examination.removeExam(result);
                    result = null;
                    setVisible(false);
                }
            }
        });
        tt = forErr;
        forErr.start();
    }
    private static Timer tt;

    @Action
    public void breakExam() {
        Uses.log.logger.warn("Отмена проведения обследования.");
        labelProgress.setText("Отмена...");
        if (timer != null) {
            timer.stop();
        }
        if (forLab != null) {
            forLab.stop();
        }
        if (loader != null) {
            loader.stop();
        }
        if (steps != null) {
            steps.cancel();
        }
        Examination.removeExam(result);
        result = null;
        doExam = false;
        setVisible(false);
    }
    public static final ReentrantLock WAIT_FOR_FINISH = new ReentrantLock();
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton buttonBreakExam;
    private javax.swing.JButton buttonCancel;
    private javax.swing.JButton buttonStart;
    private javax.swing.JCheckBox checkBoxMetronome;
    private javax.swing.JComboBox comboBoxExamKind;
    private javax.swing.JComboBox comboBoxInsideKind;
    private javax.swing.JComboBox comboBoxMetroKind;
    private javax.swing.JComboBox comboBoxMetronome;
    private javax.swing.JComboBox comboBoxMinutes;
    private javax.swing.JComboBox comboBoxShoesKind;
    private javax.swing.JComboBox comboBoxTime;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel labelMetronome;
    private javax.swing.JLabel labelProgress;
    private javax.swing.JLabel labelState;
    private javax.swing.JLabel labelTime;
    private javax.swing.JPanel panelBattons;
    private javax.swing.JSlider sliderMetronome;
    private javax.swing.JSlider sliderTime;
    private javax.swing.JTextArea textAreaExamDescription;
    private javax.swing.JToggleButton toggleButtonTestMetronome;
    // End of variables declaration//GEN-END:variables
}
